from django.core.paginator import Paginator, EmptyPage
from django.core.urlresolvers import reverse
from django.shortcuts import render, redirect
from goods.models import *
from django.core.cache import cache
import json

# Create your views here.
from django.views.generic import View
from django_redis import get_redis_connection

class BaseCartView(View):
    """每个页面都有购物车数据，因此将购物车数据封装为几类，页面继承"""

    def get_cart_num(self, request):
        """获取购物车的数量"""
        cart_num = 0
        # 如果用户登陆，就从redis中获取购物车数据
        if request.user.is_authenticated():
            # 创建redis_conn对象
            redis_conn = get_redis_connection('default')
            # 获取用户id
            user_id = request.user.id
            # 从数据库中查询数据，没有则返回None
            cart = redis_conn.hgetall('cart_%s' % user_id)

            # 遍历购物车字典，取出数据
            for val in cart.values():
                cart_num += int(val)

        else:
            # 如果用户未登陆，则从cookie中获取
            cart_json = request.COOKIES.get('cart')  # json字符串
            # 判断是不是存在
            if cart_json:
                # 转化为json字典
                cart_dict = json.loads(cart_json)
            else:
                cart_dict = {}

            # 遍历购物车字典
            for val in cart_dict.values():
                cart_num += val

        return cart_num


class ListView(BaseCartView):
    """商品列表"""
    def get(self, request, category_id, page_num):
        """处理get请求"""

        # 获取sort参数， 如果用户没有传，择是默认的排序（按照数据库的id）
        sort = request.GET.get('sort', 'default')
        # 校验参数
        # 判断category_id是否正确，通过异常来判断
        # 查询的时候要防止id不存在的时候
        try:
            category = GoodsCategory.objects.get(id=category_id)
        except GoodsCategory.DoesNotExist:
            # 重定向到主页
            return redirect(reverse('goods:index'))

        # 查询商品的所有类别
        # ｄｊａｎｇｏ为惰性查询，不会一下子将所有数据查出来，而是在渲染的时候
        # 智能的查询需要的数据
        categories = GoodsCategory.objects.all()
        # 查询该类别商品的新品推荐
        new_skus = GoodsSKU.objects.filter(category=category).order_by('-create_time')[:2]

        # 查询所有商品的sku信息，按照排序规则来查询
        if sort == 'price':
            # 按照价格排序
            skus = GoodsSKU.objects.filter(category=category).order_by('price')
        elif sort == 'hot':
            # 按照热度排序, 注意反向
            skus = GoodsSKU.objects.filter(category=category).order_by('-sales')

        else:
            # 处理默认情况或者用户传入的其他不规则情况
            # 都按照默认顺序排序
            skus = GoodsSKU.objects.filter(category=category)
            # 重置sort， 不能出现sort = dfadasf
            sort = 'default'

        # 实现分页, 每页有两个数据
        paginator = Paginator(skus, 2)

        # 校验页码是不是存在， 只有知道分页对象， 才能知道page_num是否正确
        # 防止用户输入过大的页码
        try:
            # 函数返回当前页的商品对象列表
            page_skus = paginator.page(page_num)
        except EmptyPage:
            # 默认返回第一页
            page_skus = paginator.page(1)

        # 获取页数列表
        page_list = paginator.page_range

        # 购物车
        cart_num = self.get_cart_num(request)

        # # 如果是登陆用户，获取购物车数量
        # if request.user.is_authenticated():
        #     # 获取id
        #     user_id = request.user.id
        #     # 从redis中获取购物车信息
        #     redis_conn = get_redis_connection('default')
        #     # 如果没有，返回None
        #     # 从哈希型直接读取为字典型
        #     cart_dict = redis_conn.hgetall('cart_%s' % user_id)
        #     for val in cart_dict.values():
        #         cart_num += int(val)

        # 构造上下文
        context = {
            'sort': sort,
            'category': category,
            'cart_num': cart_num,
            'categories': categories,
            'new_skus': new_skus,
            'page_skus': page_skus,
            'page_list':page_list,
        }

        # 渲染模板
        return render(request, 'list.html', context)





class IndexView(BaseCartView):
    """主页"""

    def get(self, request):
        """查询商品信息，并渲染"""


        # 查看有没有缓存
        # context是字典型数据，在redis中以哈希类型存储
        context = cache.get('index_page_data')
        # 设置缓存时只将数据库中的内容缓存，减少和数据库的交互

        if context is None:


            # 全部商品分类
            categories = GoodsCategory.objects.all()

            # 轮播图展示栏
            banners = IndexGoodsBanner.objects.all().order_by('index')

            # 推广商品展示栏
            promotions = IndexPromotionBanner.objects.all().order_by('index')

            # 具体商品列表
            # 根据category查询商品
            for category in categories:
                title_banners = IndexCategoryGoodsBanner.objects.filter(
                    category=category).filter(display_type=0).order_by('index')
                category.title_banners = title_banners
                # 通过给实例化对象添加属性的方式传递
                image_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=1).order_by('index')
                category.image_banners = image_banners[0:4]
            context = {
                'categories': categories,
                'banners': banners,
                'promotions': promotions,
            }

            # 设置缓存数据：名字，内容，有效期
            cache.set('index_page_data', context, 3600)

        # 购物车信息不能加入缓存，查询购物车信息

        cart_num = self.get_cart_num(request)
        # 如果是登陆用户，就获取购物车的数据
        ####已经通过get_cart_num读取，以下代码可以忽略
        # if request.user.is_authenticated():
        #     # 创建redis链接
        #     redis_conn = get_redis_connection('default')
        #     user_id = request.user.id
        #     # 从redis中查询数据，返回字典
        #     cart_dict = redis_conn.hgetall('cart_%s'%user_id)
        #     # 遍历购物车字典的值，累加购物车的值
        #     for value in cart_dict.values():
        #         cart_num += int(value)

        # 后台修改了数据时也要修改缓存
        # 刷新购物车数据
        context.update(cart_num=cart_num)

        return render(request, 'index.html', context)


class DetailView(BaseCartView):
    """商品详细信息页面"""

    def get(self, request, sku_id):
        # 尝试获取缓存数据
        context = cache.get("detail_%s" % sku_id)
        # print(sku_id) 成功
        # 如果缓存不存在
        if context is None:
            try:
                sku = GoodsSKU.objects.get(id=sku_id)
            except:
                # from django.http import http404
                # raise Http404("商品不存在")
                return redirect(reverse("goods:index"))
            # 获取类别
            categories = GoodsCategory.objects.all()

            # 从订单中获取评论信息
            sku_orders = sku.ordergoods_set.all().order_by('-create_time')
            if sku_orders:
                for sku_order in sku_orders:
                    # 为实例对象添加属性
                    sku_order.ctime = sku_order.create_time.strftime('%Y-%m-%d %H:%M:%S')
                    # 明确各个数据表之间的关系
                    sku_order.username = sku_order.order.user.username
            else:
                sku_orders = []

            # 获取新品推荐
            new_skus = GoodsSKU.objects.filter(category=sku.category).order_by("-create_time")[:2]

            # 获取其他规格的商品
            other_skus = sku.goods.goodssku_set.exclude(id=sku_id)

            context = {
                "categories": categories,
                'sku': sku,
                'orders': sku_orders,
                'new_skus': new_skus,
                'other_skus': other_skus,
            }

            # 设置缓存
            cache.set('detail_%s'%sku_id, context, 3600)

        cart_num = self.get_cart_num(request)

        # 添加浏览记录
        if request.user.is_authenticated():
            # 获取用户id
            user_id = request.user.id
            # 从redis中获取购物车的信息
            redis_conn = get_redis_connection('default')
            # redis中购物车的数据按照hash的方式存储，cart_id为key，里面的值为商品skuid和数量构成的键值对
            # 如果没有，返回None
            # cart_dict = redis_conn.hgetall('cart_%s' % user_id)
            # for val in cart_dict.values():
            #     cart_num += int(val)

            # 浏览记录： lpush，最新的在最前面
            # 浏览记录的存储方式为列表，key为history_id
            # 浏览新商品时，如果里面有记录，删除
            redis_conn.lrem('history_%s' % user_id, 0, sku_id)
            # 添加新的记录
            redis_conn.lpush('history_%s' % user_id, sku_id)
            # 至保存5条记录
            redis_conn.ltrim('history_%s' % user_id, 0, 4)

        context.update({'cart_num': cart_num})
        return render(request, 'detail.html', context)


